package proxy;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.Objects;

public class GPProxy {
    protected GPInvocationHandler h;
    protected GPProxy(GPInvocationHandler h) {
        this.h = h;
    }
    public static Object newProxyInstance(GPClassLoader loader,
                                          Class<?>[] interfaces,
                                          GPInvocationHandler h)
            throws IllegalArgumentException
    {
        String src=genernateSrc(interfaces);
        String filePath=GPProxy.class.getResource("").getPath();
        File f=new File(filePath+"$Proxy0.java");
        FileWriter fw= null;
        try {
            fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            JavaCompiler compiler= ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager=compiler.getStandardFileManager(null,null,null);
            Iterable iterable=manager.getJavaFileObjects(f);

            JavaCompiler.CompilationTask task=compiler.getTask(null,manager,null,null,null,iterable);
            task.call();
            manager.close();

            Class proxyClass=loader.findClass("$Proxy0");
            Constructor c=proxyClass.getConstructor(GPInvocationHandler.class);
            return c.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String genernateSrc(Class<?>[] interfaces) {
        StringBuilder stringBuilder=new StringBuilder();
        stringBuilder.append("package proxy;\n" +
                "import java.lang.reflect.InvocationHandler;\n" +
                "import java.lang.reflect.Method;\n" +
                "import java.lang.reflect.Proxy;\n" +
                "import java.lang.reflect.UndeclaredThrowableException;\n" +
                "import proxy.Dog;\n" +
                "\n" +
                "public final class $Proxy0 extends GPProxy implements Dog {\n" +
                "    private static Method m1;\n" +
                "    private static Method m3;\n" +
                "    private static Method m2;\n" +
                "    private static Method m0;\n" +
                "\n" +
                "    public $Proxy0(GPInvocationHandler var1) {\n" +
                "        super(var1);\n" +
                "    }\n" +
                "\n" +
                "    public final boolean equals(Object var1) {\n" +
                "        try {\n" +
                "            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});\n" +
                "        } catch (RuntimeException | Error var3) {\n" +
                "            throw var3;\n" +
                "        } catch (Throwable var4) {\n" +
                "            throw new UndeclaredThrowableException(var4);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public final void run() {\n" +
                "        try {\n" +
                "            super.h.invoke(this, m3, (Object[])null);\n" +
                "        } catch (RuntimeException | Error var2) {\n" +
                "            throw var2;\n" +
                "        } catch (Throwable var3) {\n" +
                "            throw new UndeclaredThrowableException(var3);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public final String toString() {\n" +
                "        try {\n" +
                "            return (String)super.h.invoke(this, m2, (Object[])null);\n" +
                "        } catch (RuntimeException | Error var2) {\n" +
                "            throw var2;\n" +
                "        } catch (Throwable var3) {\n" +
                "            throw new UndeclaredThrowableException(var3);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public final int hashCode() {\n" +
                "        try {\n" +
                "            return (Integer)super.h.invoke(this, m0, (Object[])null);\n" +
                "        } catch (RuntimeException | Error var2) {\n" +
                "            throw var2;\n" +
                "        } catch (Throwable var3) {\n" +
                "            throw new UndeclaredThrowableException(var3);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    static {\n" +
                "        try {\n" +
                "            m1 = Class.forName(\"java.lang.Object\").getMethod(\"equals\", Class.forName(\"java.lang.Object\"));\n" +
                "            m3 = Class.forName(\"proxy.Dog\").getMethod(\"run\");\n" +
                "            m2 = Class.forName(\"java.lang.Object\").getMethod(\"toString\");\n" +
                "            m0 = Class.forName(\"java.lang.Object\").getMethod(\"hashCode\");\n" +
                "        } catch (NoSuchMethodException var2) {\n" +
                "            throw new NoSuchMethodError(var2.getMessage());\n" +
                "        } catch (ClassNotFoundException var3) {\n" +
                "            throw new NoClassDefFoundError(var3.getMessage());\n" +
                "        }\n" +
                "    }\n" +
                "}");
        return stringBuilder.toString();
    }

    public static void main(String[] args) {
        GPProxy.newProxyInstance(null,null,null);
    }
}
